﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Collections2
{ // 1

    public enum DictNumber { One = 1, Two = 2 }

    /// <summary>
    /// In the two types of TwoWayDictionary each key is also a value and each value is also a key.
    /// Inside TwoWayDictionary there are 2 Dictionaries. One with keys of type S and values of type T,
    /// and one with keys of type T and values of type S.
    /// There is only a one-to-one correspondance betwwen these 2 Dictionaries.
    /// For TwoWayDictionary&lt;S, T&gt; version of TwoWayDictionary the typeof(S) can not = typeof(T).
    /// </summary>
    /// <typeparam name="S"></typeparam>
    /// <typeparam name="T"></typeparam>
    
    public class TwoWayDictionary<S, T> 
    { // 2
        Dictionary<S, T> Ss;
        Dictionary<T,S> Ts;

        public TwoWayDictionary()
        { // 3
            if (typeof(S).Name == typeof(T).Name)
            {
                throw new TwoWayDictException(3, "For TwoWayDictionary<S,T>, the typeof(S) can not = typeof(T).");
            }
            Ss = new Dictionary<S,T>();
            Ts = new Dictionary<T,S>();
        }// 3

       

        
        /// <summary>
        /// Gets the underlying Dictionary&lt;S,T&gt;
        /// </summary>
        /// <returns>The underlying Dictionary&lt;S,T&gt;</returns>
        
        public Dictionary<S, T> GetSs()
        {
            return Ss; 
        }

        
        /// <summary>
        /// Gets the underlying Dictionary&lt;T,S&gt;
        /// </summary>
        /// <returns>The underlying Dictionary&lt;T,S&gt;</returns>
        
        public Dictionary<T, S> GetTs()
        {
            return Ts; 
        }

        public bool Contains(S _S)
        {
            return Ss.Keys.Contains(_S);
        }

        public bool Contains(T _T)
        {
            return Ts.Keys.Contains(_T);
        }

        public int Count
        {
            get { return Ss.Count; }
        }

        public void Remove(S _S)
        {
            if (this.Contains(_S))
            {
                T _T = Ss[_S];
                Ss.Remove(_S);
                Ts.Remove(_T);
            }
            else
            {
                throw new TwoWayDictException(1, "The key = " + _S.ToString() + " of type " + typeof(S).Name + " does not exist!");
            }
        }

        public void Remove(T _T)
        {
            if (this.Contains(_T))
            {
                S _S = Ts[_T];
                Ss.Remove(_S);
                Ts.Remove(_T);
            }
            else
            {
                throw new TwoWayDictException(2, "The key = " + _T.ToString() + " of type " + typeof(T).Name + " does not exist!");
            }

        }

        public void Clear()
        {
            Ss = new Dictionary<S, T>();
            Ts = new Dictionary<T, S>();         
        }

        protected void Init(IEnumerable _Ss, IEnumerable _Ts)
        {
            IEnumerator senumer = _Ss.GetEnumerator();
            IEnumerator tenumer = _Ts.GetEnumerator();

            int SCount = 0;
            int TCount = 0;

            while (senumer.MoveNext())
            { // 4
                SCount++;
            } // 4

            while (tenumer.MoveNext())
            {// 4
                TCount++;
            }// 4

            if (SCount != TCount) { throw new TwoWayDictException(3, "The length of the 2 IEnumerable parameters _Ss and _Ts are not equal!"); }

            // if ((SCount == 0) || (TCount == 0)) { throw new TwoWayDictException("The length of the parametrs _Ss and _Ts must not = 0!"); }

            senumer.Reset();
            tenumer.Reset();

            if ((SCount == TCount) && (SCount != 0))
            { //4
                while ((senumer.MoveNext()) && (tenumer.MoveNext()))
                {// 5
                    Ss.Add((S)senumer.Current, (T)tenumer.Current);
                    Ts.Add((T)tenumer.Current, (S)senumer.Current);
                }// 5

            }// 4


        }

        
        /// <summary>
        /// The parameters _Ss and _Ts can be any IEnumerable of types S and T. This means you can use both non-generic
        /// (Ex: ArrayList, regular arrays) and generic (Ex: List&lt;int&gt;) collections
        /// </summary>
        /// <param name="_Ss"></param>
        /// <param name="_Ts"></param>
        
        
        public TwoWayDictionary(IEnumerable _Ss, IEnumerable _Ts)
        { // 3
            if (typeof(S).Name == typeof(T).Name)
            {
                throw new TwoWayDictException(3, "For TwoWayDictionary<S,T>, the typeof(S) can not = typeof(T).");
            }

            Ss = new Dictionary<S, T>();
            Ts = new Dictionary<T, S>();

            Init(_Ss, _Ts);
        } //3

        
        /// <summary>
        /// Combines and appends the IEnumerables _Ss and _Ts to the already existing TwoWayDictionary. 
        /// </summary>
        /// <param name="_Ss"></param>
        /// <param name="_Ts"></param>
        public void Append(IEnumerable _Ss, IEnumerable _Ts)
        {
            Init(_Ss, _Ts);
            

        }


            public void Add(S _s, T _t)
            {// 3
                Ss.Add(_s,_t);
                Ts.Add(_t,_s);
            }// 3

            public S this[T _t]
            {// 3
                get 
                {
                    if (this.Contains(_t))
                    {
                        return Ts[_t];
                    }
                    else
                    {
                        throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                    }
                }
                set { Ts[_t] = value;}
            }// 3

             public T this[S _s]
            {// 3
                get
                {
                    if (this.Contains(_s))
                    {
                        return Ss[_s];
                    }
                    else
                    {
                        throw new TwoWayDictException(4, "The key = " + _s.ToString() + " of type " + _s.GetType().Name + " not found.");
                    }
                }
                set { Ss[_s] = value;}
            }// 3

        } // 2 End TwoWayDictionary<S,T>
    
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class TwoWayDictionary<T>
    {
        Dictionary<T, T> T1s;
        Dictionary<T, T> T2s;

        public TwoWayDictionary()
        {
            T1s = new Dictionary<T, T>();
            T2s = new Dictionary<T, T>();
        }

        protected void Init(IEnumerable _T1s, IEnumerable _T2s)
        {
            IEnumerator t1enumer = _T1s.GetEnumerator();
            IEnumerator t2enumer = _T2s.GetEnumerator();

            int T1Count = 0;
            int T2Count = 0;

            while (t1enumer.MoveNext())
            { // 4
                T1Count++;
            } // 4

            while (t2enumer.MoveNext())
            {// 4
                T2Count++;
            }// 4

            t1enumer.Reset();
            t2enumer.Reset();

            if ((T1Count == T2Count) && (T1Count != 0))
            { //4
                while ((t1enumer.MoveNext()) && (t2enumer.MoveNext()))
                {// 5
                    T1s.Add((T)t1enumer.Current, (T)t2enumer.Current);
                    T2s.Add((T)t2enumer.Current, (T)t1enumer.Current);
                }// 5

            }// 4

        }

         public TwoWayDictionary(IEnumerable _T1s, IEnumerable _T2s)
        { // 3
            T1s = new Dictionary<T, T>();
            T2s = new Dictionary<T, T>();

            Init(_T1s, _T2s);
            
        } //3

         /// <summary>
         /// Combines and appends the IEnumerables _T1s and _T2s to the already existing TwoWayDictionary. 
         /// </summary>
         /// <param name="_Ss"></param>
         /// <param name="_Ts"></param>
         public void Append(IEnumerable _T1s, IEnumerable _T2s)
         {
             Init(_T1s, _T2s);


         }


            public void Add(T _t1, T _t2)
            {// 3
                T1s.Add(_t1,_t2);
                T2s.Add(_t2,_t1);
            }// 3

            public T this[T _t]
            {
                get
                {
                    if (T1s.Keys.Contains(_t))
                    {
                        return T1s[_t];
                    }

                    if (T2s.Keys.Contains(_t))
                    {
                        return T2s[_t];
                    }

                    throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                } // End get

                set
                {
                    if (T1s.Keys.Contains(_t))
                    {
                        this[_t, DictNumber.One] = value;
                    }

                    if (T2s.Keys.Contains(_t))
                    {
                        this[_t, DictNumber.Two] = value;
                    }

                    if (!(T1s.Keys.Contains(_t)) && !(T2s.Keys.Contains(_t)))
                    {
                        throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                    }
                } // End set
            }

            public T this[T _t, DictNumber _dictnumber]
            {
                get
                {
                    if (_dictnumber == DictNumber.One)
                    {
                        if (T1s.Keys.Contains(_t))
                        {
                            return T1s[_t];
                        }
                    }

                    if (_dictnumber == DictNumber.Two)
                    {
                        if (T2s.Keys.Contains(_t))
                        {
                            return T2s[_t];
                        }
                    }


                    throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                } // End get

                set
                {
                    if (_dictnumber == DictNumber.One)
                    {
                        if (T1s.Keys.Contains(_t))
                        {
                            T temp = T1s[_t];
                            T2s.Remove(temp);
                            T1s[_t] = value;
                            T2s.Add(value, _t);

                        }
                        else
                        {
                            throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                        }
                    } // End if DictNumber.One

                    if (_dictnumber == DictNumber.Two)
                    {
                        if (T2s.Keys.Contains(_t))
                        {
                            T temp = T2s[_t];
                            T1s.Remove(temp);
                            T2s[_t] = value;
                            T1s.Add(value, _t);

                        }
                        else
                        {
                            throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                        }
                    } // End if DictNumber.One


                }

            }

            /*
            public T this[T _t, DictNumber _dictnum]
            {
                get
                {
                    if (_dictnum == DictNumber.One)
                    {
                        return this[_t];
                    }
                    else
                    {
                        if (T2s.Keys.Contains(_t))
                        {
                            return T2s[_t];
                        }
                        else
                        {
                            throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                        }
                    }
                } // End get

                set
                {
                    if (_dictnum == DictNumber.One)
                    {
                        this[_t] = value;
                    }
                    else
                    {
                        if (T2s.Keys.Contains(_t))
                        {
                            T temp = T2s[_t];
                            // T1s.Remove(_t);
                            T1s.Remove(temp);
                            T2s[_t] = value;
                            T1s.Add(value, _t);
                        }
                        else
                        {
                            throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                        }
                    }
                }
            }
            
            public T this[T _t]
            {// 3
                get 
                { 
                    if (T1s.Keys.Contains(_t))
                    {
                        return T1s[_t];
                    }
                    else
                    {
                        throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                    }
                   
                }
                set 
                { 
                    if (T1s.Keys.Contains(_t))
                    {
                        T temp = T1s[_t];
                        // T1s.Remove(_t);
                        T2s.Remove(temp);
                        T1s[_t] = value;
                        T2s.Add(value,_t);
                    }
                    else
                    {
                        throw new TwoWayDictException(4, "The key = " + _t.ToString() + " of type " + _t.GetType().Name + " not found.");
                    }
                    
                    
                }
            }// 3
            */
            

        } // 2 // End TwoWayDictionary<T>
    
    }


